﻿namespace PomodoroTool
{
    #region

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;
    using System.Windows.Media.Animation;
    using System.Windows.Threading;

    using Core;

    #endregion

    /// <summary>
    /// Interaction logic for Timer.xaml
    /// </summary>
    public partial class TimerWindow : Window, ITimerView
    {
        #region Constants and Fields

        public TimerPresenter Presenter;

        private readonly DispatcherTimer _updateTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) };

        private Storyboard _slideIn;

        private Storyboard _slideOut;

        private IObservable<IEvent<MouseEventArgs>> _onMouseIn;

        private IObservable<IEvent<MouseEventArgs>> _onMouseOut;

        #endregion

        #region Constructors and Destructors

        public TimerWindow()
        {
            this.InitializeComponent();
            _updateTimer.Tick += this._updateTimer_Tick;
            _updateTimer.Start();
            this.InitAnimation();
        }

        #endregion

        #region Implemented Interfaces

        #region ITimerView

        public bool AskPomodoroDone()
        {
            return MessageBox.Show("Add pomodoro mark to active task?", "", MessageBoxButton.YesNo) ==
                   MessageBoxResult.Yes;
        }

        public void SetPauseButtonVisible(bool visible)
        {
            this.btnPause.Visibility = visible ? Visibility.Visible : Visibility.Collapsed;
        }

        public void SetStartButtonVisible(bool visible)
        {
            this.btnStart.Visibility = visible ? Visibility.Visible : Visibility.Collapsed;
        }

        public void SetTimeLeft(TimeSpan timeLeft)
        {
            var dateTime = DateTime.MinValue.Add(timeLeft);
            this.txtTimeLeft.Text = String.Format("{0:mm}", dateTime);
        }

        public void ShowBreakDoneMessageBox()
        {
            this.Activate();
            MessageBox.Show("Break finished, do something.");
        }

        public void ShowDoneMessageBox()
        {
            this.Activate();
            MessageBox.Show("Pomodoro done, take a break.");
        }

        #endregion

        #endregion

        #region Methods

        private Storyboard CreateSlideAnimation(double widthFrom, double widthTo)
        {
            var doubleAnimation = new GridLengthAnimation();
            doubleAnimation.Duration = TimeSpan.FromMilliseconds(300);
            doubleAnimation.RepeatBehavior = new RepeatBehavior(1);
            doubleAnimation.AutoReverse = false;
            doubleAnimation.ReverseValue = widthTo;
            doubleAnimation.From = new GridLength(widthFrom);
            doubleAnimation.To = new GridLength(widthTo);

            var story = new Storyboard();
            story.Children.Add(doubleAnimation);
            Storyboard.SetTargetName(doubleAnimation, this.buttonsColumn.Name);
            Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath(ColumnDefinition.WidthProperty));
            return story;
        }

        private void InitAnimation()
        {
            _slideOut = CreateSlideAnimation(0.0, this.buttonsColumn.Width.Value);
            _slideIn = CreateSlideAnimation(this.buttonsColumn.Width.Value, 0.0);
            _onMouseIn = Observable.FromEvent<MouseEventArgs>(this, "MouseEnter");
            _onMouseOut = Observable.FromEvent<MouseEventArgs>(this, "MouseLeave");

            _onMouseIn.ObserveOnDispatcher().Subscribe((e) => _slideOut.Begin(this));

            _onMouseOut
                .Timestamp()
                .Delay(TimeSpan.FromSeconds(5))
                .CombineLatest(_onMouseIn.Timestamp(), (l, r) => l.Timestamp > r.Timestamp ? true : false)
                .Where(q => q)
                .ObserveOnDispatcher()
                .Subscribe((e) => _slideIn.Begin(this));
        }

        private void Window_Activated(object sender, EventArgs e)
        {
            _updateTimer.Interval = TimeSpan.FromMilliseconds(250);
        }

        private void _updateTimer_Tick(object sender, EventArgs e)
        {
            this.Presenter.OnUpdateTimeLeft();
        }

        private void btnPause_Click(object sender, RoutedEventArgs e)
        {
            this.Presenter.OnPause();
        }

        private void btnStart_Click(object sender, RoutedEventArgs e)
        {
            this.Presenter.OnStart();
        }

        private void btnStop_Click(object sender, RoutedEventArgs e)
        {
            this.Presenter.OnStop();
        }

        private void btnTasks_Click(object sender, RoutedEventArgs e)
        {
            Presenter.OnShowTasks();
        }

        private void txtTimeLeft_MouseUp(object sender, MouseButtonEventArgs e)
        {
            this.Presenter.OnTimerClick();
        }

        #endregion

        private void Window_Deactivated(object sender, EventArgs e)
        {
            _updateTimer.Interval = TimeSpan.FromSeconds(30);
        }
    }

    public class TimerPresenter
    {
        #region Constants and Fields

        private readonly PomodoroTimer _breakTimer = new PomodoroTimer(TimeSpan.FromMinutes(5));

        private readonly PomodoroController _controller;

        private readonly PomodoroTimer _pomodoroTimer = new PomodoroTimer(TimeSpan.FromMinutes(25));

        private readonly ITimerView _view;

        private PomodoroTimer _activeTimer;

        #endregion

        #region Constructors and Destructors

        public TimerPresenter(ITimerView view, PomodoroController controller)
        {
            _view = view;
            _controller = controller;
        }

        #endregion

        #region Events

        public event Action ShowTasks = delegate { };

        #endregion

        #region Public Methods

        public void Init()
        {
            _activeTimer = _pomodoroTimer;
            _activeTimer.Stop();
            _pomodoroTimer.SetCallback(this.OnPomodoroTimerEnd);
            _breakTimer.SetCallback(this.OnBreakTimerEnd);

            _view.SetPauseButtonVisible(false);
            _view.SetStartButtonVisible(true);
        }

        public void OnPause()
        {
            _view.SetStartButtonVisible(true);
            _view.SetPauseButtonVisible(false);
            this._activeTimer.Pause();
        }

        public void OnShowTasks()
        {
            this.OnPause();
            ShowTasks();
        }

        public void OnStart()
        {
            _view.SetStartButtonVisible(false);
            _view.SetPauseButtonVisible(true);
            this._activeTimer.Start();
        }

        public void OnStop()
        {
            bool stopped = this._activeTimer.Stopped;
            if (!stopped)
            {
                this._activeTimer.Stop();
            }

            if (_activeTimer == _pomodoroTimer && !stopped)
            {
                if (_controller.HasActivePomodoro && _view.AskPomodoroDone())
                {
                    _controller.PomodoroDone();
                }
            }

            _view.SetPauseButtonVisible(false);
            _view.SetStartButtonVisible(true);
        }

        public void OnTimerClick()
        {
            OnStop();

            this._activeTimer = this._activeTimer == this._pomodoroTimer ? this._breakTimer : this._pomodoroTimer;
            OnUpdateTimeLeft();
        }

        public void OnUpdateTimeLeft()
        {
            _view.SetTimeLeft(this._activeTimer.TimeLeft);
        }

        #endregion

        #region Methods

        private void OnBreakTimerEnd()
        {
            _view.ShowBreakDoneMessageBox();
            this.OnStop();
        }

        private void OnPomodoroTimerEnd()
        {
            _controller.PomodoroDone();
            _view.ShowDoneMessageBox();
            this.OnStop();
        }

        #endregion
    }

    public interface ITimerView
    {
        #region Public Methods

        bool AskPomodoroDone();

        void SetPauseButtonVisible(bool visible);

        void SetStartButtonVisible(bool visible);

        void SetTimeLeft(TimeSpan timeLeft);

        void ShowBreakDoneMessageBox();

        void ShowDoneMessageBox();

        #endregion
    }
}